"""
    Windows module.
    
    In this module all the Windows are defined.
    There are two windows at the moment, MainWindow and QuestionWindow.
    (GenerateWindow is still under development and I'm not sure if it's really needed)

"""
from Tkinter import *
import tkMessageBox, tkFileDialog, os

SQRT = u"\u221a"

about_text = "py4math is een systeem om wiskunde mee te oefenen.\n\
py4math: Het spijkerbord richt zich op de mogelijkheden die het spijkerbord biedt.\n\
v0.1 Copyright " + u'\u00A9' + " 2008."

img_dir = os.path.join('data', 'img')

def center_screen(widget):
    """

    Centers the window in the screen and returns a tuple of the (width, height,  x, y) to geometry()
    widget = name of the window

    """
    top = widget.winfo_toplevel()
    sw = top.winfo_screenwidth()
    sh = top.winfo_screenheight()
    w = top.winfo_width()
    h = top.winfo_height()
    x = (sw - w)/2
    y = (sh - h)/2
    geom = '%dx%d+%d+%d' % (w, h, x, y)
    return geom

class MainWindow:
    """

    The main window. Displays an intro image and a list of available subjects. After choosing a subject and clicking 'kies', the user can
    choose an exercise. 

    """
    def __init__(self, master):
        """
        Main initialization. The image is displayed and init_subject_choice() gets called
	"""
        self.e = StringVar()          #Variables  
        self.s = StringVar()          #for
        self.d = StringVar()          #Radiobuttons
        self.custom = False #Default is no custom xml file
        frame = Frame(master)
        frame.grid()
        self.master = master
        master.protocol("WM_DELETE_WINDOW", self.close) #If window is destroyed manually make sure self.close is called so self.exit is set to True 
        Label(frame, text="Welkom bij py4math: Het spijkerbord!", font=("Helvetica", 15)).grid(row=0)
        photo = PhotoImage(file=os.path.join(img_dir,'front.GIF'))
        img = Label(frame, image=photo)
        img.photo = photo
        img.grid(row=1)

        menubar = Menu(self.master)
        filemenu = Menu(menubar, tearoff=0)
        filemenu.add_command(label="Open zelfgemaakt XML bestand...", command=self.custom_xml_file)
        filemenu.add_separator()
        filemenu.add_command(label="Afsluiten", command=lambda:self.close())
        menubar.add_cascade(label="Bestand", menu=filemenu)
        helpmenu = Menu(menubar, tearoff=0)
        helpmenu.add_command(label="Over...", command=lambda:tkMessageBox.showinfo("Over Wielo", about_text))
        menubar.add_cascade(label="Help", menu=helpmenu)
        self.master.config(menu=menubar)

        self.init_subject_choice()

    def next_frame(self, step):
        """
        This method decides what frame to display next. It checks whether the radiobutton variables are set
	and chooses the right frame.
	"""
        if step == 1:
            if self.custom:
                self.exit = False
                self.master.destroy()
                return
            if self.e.get() == "":
                self.init_exercise_choice()
            elif self.e.get() != "" and self.d.get() == "":
                self.init_difficulty_choice()
            elif self.e.get() != "" and self.d.get() != "":
                self.exit = False
                self.master.destroy()
        elif step == -1:
            if self.e.get() != "":
                self.init_subject_choice()

    def init_subject_choice(self):
        """
        This method initializes the subject choice frame. It first checks if the other frames are still up and destroys them if they are.
	Then two radiobuttons are used to let the user choose between subjects.
	"""
        if self.e.get() != "": 
            self.ExerciseFrame.destroy()
            self.Bframe.destroy()
            self.e.set("")
        try:
            self.DifficultyFrame.destroy()
        except:
            pass
        self.s.set("sbord")
        self.SubjectFrame = Frame(self.master, bd=5, relief=GROOVE)
        self.SubjectFrame.grid()
        Label(self.SubjectFrame, text="Selecteer het onderwerp:", font=("Helvectica",12)).grid()
        Radiobutton(self.SubjectFrame, text="Spijkerbord", variable=self.s, value="sbord").grid(sticky=W)
        Radiobutton(self.SubjectFrame, text="Gelijkvormigheid", variable=self.s, value="gvorm").grid(sticky=W)
        self.init_buttons()

    def init_exercise_choice(self):
        """
        This method initializes the exercise choice frame. It first checks if there are any other frames active, and destroys them.
	Then the variable from the subject choice (s) is checked if it contains either "sbord" or "gvorm"
	and then displays the correct radiobuttons.
	"""
        try:
            self.DifficultyFrame.destroy()
            self.d.set("")
        except:
            pass
        self.SubjectFrame.destroy()
        self.Bframe.destroy()
        self.ExerciseFrame = Frame(self.master, bd=5, relief=GROOVE)
        self.ExerciseFrame.grid()
        Label(self.ExerciseFrame, text="Selecteer de oefening:", font=("Helvectica",12)).grid(sticky=N+S+E+W)
        if self.s.get() == "sbord":
            self.e.set("1")
            Radiobutton(self.ExerciseFrame, text="1: Zijde vierkant bepalen", variable=self.e, value="1").grid(sticky=W)
            Radiobutton(self.ExerciseFrame, text="2: Schuine zijde driehoek bepalen", variable=self.e, value="2").grid(sticky=W)
            Radiobutton(self.ExerciseFrame, text="3: Rechte zijde driehoek bepalen", variable=self.e, value="3").grid(sticky=W)
            Radiobutton(self.ExerciseFrame, text="4: Lengte lijnstuk bepalen", variable=self.e, value="4").grid(sticky=W)
            Radiobutton(self.ExerciseFrame, text="5: Trapezium", variable=self.e, value="5").grid(sticky=W)
        elif self.s.get() == "gvorm":
            self.e.set("6")
            Radiobutton(self.ExerciseFrame, text="Driehoek binnen een driehoek (vergrotingsfactor)", variable=self.e, value="6").grid(sticky=W)
            Radiobutton(self.ExerciseFrame, text="Zandloper-constructie", variable=self.e, value="7").grid(sticky=W)
            Radiobutton(self.ExerciseFrame, text="Vergrotingsfactor II", variable=self.e, value="8").grid(sticky=W)
            Radiobutton(self.ExerciseFrame, text="Vergrotingsfactor III", variable=self.e, value="9").grid(sticky=W)
        self.init_buttons()

    def init_difficulty_choice(self):
        """
	This method initializes the difficulty selection frame after checking if other frames are still active.
	"""
        try:
            self.SubjectFrame.destroy()
            self.ExerciseFrame.destroy()
        except:
            pass
        self.Bframe.destroy()
        self.DifficultyFrame = Frame(self.master, bd=5, relief=GROOVE)
        self.DifficultyFrame.grid()
        Label(self.DifficultyFrame, text="Selecteer je moeilijkheidsgraad", font=("Helvectica",12)).grid(sticky=N+S+E+W)
        self.d.set("1")
        Radiobutton(self.DifficultyFrame, text="Makkelijk (Met tussenstappen)", variable=self.d, value="1").grid(sticky=W)
        Radiobutton(self.DifficultyFrame, text="Moeilijk (Zonder tussenstappen)", variable=self.d, value="0").grid(sticky=W)
        self.init_buttons()

    def init_buttons(self):
        """
        This method initializes the buttons for the MainWindow.
	This method is called every time a frame is initialized.
	"""
        self.Bframe = Frame(self.master)
        self.Bframe.grid()
        Label(self.Bframe, text="", padx=5, pady=5).grid()
        Button(self.Bframe, text="Kies", command=lambda:self.next_frame(1)).grid(row=1)
        Button(self.Bframe, text="Terug", command=lambda:self.next_frame(-1)).grid(row=1, column=1)
        Button(self.Bframe, text="Stop", command=self.close).grid(row=1,column=2)

    def custom_xml_file(self):
        """
        This method takes care of the File Open dialog for the custom XML file.
        After the user chooses a file the variable self.custom is set to true,
        so that the mainloop in main.py knows that we're dealing with a custom file.
        """
        self.custom_xml = tkFileDialog.askopenfilename(parent=self.master, filetypes=[("XML bestanden",".xml")])
        if not self.custom_xml:
            return
        self.custom = True
        self.init_difficulty_choice()
        
    def close(self):
        """
        This method gets called when the user presses the 'stop' button.
	It sets self.exit to True, so that the main loop in main.py is properly broken
	"""
        self.exit = True
        self.master.destroy()

class QuestionWindow:
    """
    
    The Question window. This is used for questions. __init__ takes 6 arguments:
    root, the question, the geometry settings, a list [score, total_questions], the difficuly and the 'tussenstappen'.
    A canvas is drawn to draw the geometric information of the question.
    
    """

    def __init__(self, master, question, geometry, difficulty, tussenstap=None):
        """
        The main method of the QuestionWindow.
        This method takes care of the canvas, and sets the 'tussenstappen' control variables.
        Some more information on the canvas:
        First a canvas of 550x500px is created. Then a for loop takes care of the grid. 
        After that a list of point coordinates is created to pass to create_polygon(), using attribute unpacking.
        Then the names of the points are aligned in the canvas.
        Optionally, the lengths of the lines are aligned as well, but only if the XML contained <line> tags.
        Finally, the canvas is placed into the frame using .grid()
        """
        
        self.master = master
        self.tussenstap = tussenstap
        self.difficulty = difficulty
        self.nextq = True #This is set to false when the user presses the Stop button, so that the next question isn't given.
        self.master.protocol("WM_DELETE_WINDOW", self.quit) #If window is destroyed manually make sure self.quit is called so self.nextq is set to False
        
        menubar = Menu(self.master)
        filemenu = Menu(menubar, tearoff=0)
        filemenu.add_command(label="Afsluiten", command=lambda:self.quit())
        menubar.add_cascade(label="Bestand", menu=filemenu)
        helpmenu = Menu(menubar, tearoff=0)
        helpmenu.add_command(label="Over...", command=lambda:tkMessageBox.showinfo("Over Wielo", about_text))
        menubar.add_cascade(label="Help", menu=helpmenu)
        self.master.config(menu=menubar)

        QuestionFrame = Frame(master)
        QuestionFrame.grid()

        c = Canvas(master, width=550, height=500)

        for x in range(1,10):
            for y in range(1,10):
                c.create_rectangle(x*50,y*50, (x*50)+1, (y*50)+1) #Create grid

        for f in geometry['figures']:
            polyfunclist = [] #Create a list of coordinates for the points to pass to create_polygon. This way more points can be used.
            for p in f['points']:
                x = int(p['x'])
                y = int(p['y'])
                polyfunclist.append(int(x)*50)
                polyfunclist.append(abs(int(y)-10)*50)
            polyid = c.create_polygon(*polyfunclist)
            c.itemconfigure(polyid, fill='', outline='black')
        for f in geometry['figures']:
            for p in f['pixelpoints']:
                c.create_rectangle(int(p['x']), int(p['y']), int(p['x'])+1, int(p['y'])+1, fill='', outline='black')

        for f in geometry['figures']:
            for p in f['points']:
                if p['align'] in [u'top',u'topleft',u'topright']:
                    d2 = -10
                if p['align'] in [u'bottomleft',u'bottomright',u'bottom']:
                    d2 = 10
                if p['align'] in [u'topleft', u'bottomleft', u'left']:
                    d1 = -10
                if p['align'] in [u'topright', u'bottomright', u'right']:
                    d1 = 10
                if p['align'] in [u'top', u'bottom']:
                    d1 = 0
                if p['align'] in [u'left', u'right']:
                    d2 = 0
                if p['align'] == u"":
                    d1, d2 = 0,0
                c.create_text(int(p['x'])*50+d1, abs(int(p['y'])-10)*50+d2, text=p['name'])

        for f in geometry['figures']:
            for p in f['pixelpoints']:
                if p['align'] in [u'top',u'topleft',u'topright']:
                    d2 = -10
                if p['align'] in [u'bottomleft',u'bottomright',u'bottom']:
                    d2 = 10
                if p['align'] in [u'topleft', u'bottomleft', u'left']:
                    d1 = -10
                if p['align'] in [u'topright', u'bottomright', u'right']:
                    d1 = 10
                if p['align'] in [u'top', u'bottom']:
                    d1 = 0
                if p['align'] in [u'left', u'right']:
                    d2 = 0
                if p['align'] == u"":
                    d1, d2 = 0,0
                c.create_text(int(p['x'])+d1, int(p['y'])+d2, text=p['name'])
        i=0
        for l in geometry['lines']:
            c.create_text(505,50+(i*15),text="%s%s = %s" % (l['p1'], l['p2'], l['length']))
            i += 1
        i=0
        x1, y1, y2, x2 = [],[],[],[]
        for l in geometry['lines']:
            for f in geometry['figures']:
                for p in f['points']:
                    if l['p1'] == p['name']:
                        x1.append(p['x'])
                        y1.append(p['y'])
                    if l['p2'] == p['name']:
                        x2.append(p['x'])
                        y2.append(p['y'])
            linepoints = []
            for i in range(len(x1)):
                linepoints.append([[x1[i],y1[i]],[x2[i],y2[i]]])
            i = 0
            for xyxy in linepoints:
                x1 = int(xyxy[0][0])
                y1 = int(xyxy[0][1])
                x2 = int(xyxy[1][0])
                y2 = int(xyxy[1][1])                   
                xlength = abs(x1*50-x2*50)/2
                ylength = abs(abs(y1-10)*50-abs(y2-10)*50)/2
                if l['align'] in [u'top',u'topleft',u'topright']:
                    d2 = -10
                if l['align'] in [u'bottomleft',u'bottomright',u'bottom']:
                    d2 = 10
                if l['align'] in [u'topleft', u'bottomleft', u'left']:
                    d1 = -10
                if l['align'] in [u'topright', u'bottomright', u'right']:
                    d1 = 10
                if l['align'] in [u'top', u'bottom']:
                    d1 = 0
                if l['align'] in [u'left', u'right']:
                    d2 = 0
                if l['align'] == u"":
                    d1, d2 = 0,0

                if x1 == x2:
                    if y1 < y2:
                        c.create_text(x1*50+xlength+d1, abs(y2-10)*50+ylength+d2, text=l['length'])
                    elif y1 > y2:
                        c.create_text(x1*50+xlength+d1, abs(y1-10)*50+ylength+d2, text=l['length'])
                elif y1 == y2:
                    if x1 < x2:
                        c.create_text(x1*50+xlength, abs(y1-10)*50+ylength+d2, text=l['length'])
                    elif x1 > x2:
                        c.create_text(x2*50+xlength, abs(y1-10)*50+ylength+d2, text=l['length'])
                else:
                    if x1 < x2 and y1 > y2:
                        c.create_text(x1*50+xlength+d1, abs(y1-10)*50+ylength+d2, text=l['length'])
                    elif x1 < x2 and y1 < y2:
                        c.create_text(x1*50+xlength+d1, abs(y2-10)*50+ylength+d2, text=l['length'])
                i += 1
            x1, y1, y2, x2 = [],[],[],[]


        c.grid(row=0)  

        Label(master, text=question, font=("Helvetica", 16)).grid(row=0, rowspan=2, sticky=S)
        self.aantstappendone = 0
        self.aantstappen = len(self.tussenstap[0])
        self.init_tussenstap_frame()
        self.init_other_frames()

    def init_tussenstap_frame(self):
        """
        This method initializes the 'tussenstap' frame.
        First it checks if the difficulty isn't set to 0, because 0 means Hard mode - no 'tussenstappen'.
        Then it displays the correct 'tussenstap' using the aantstappendone and aantstappen variables.
        """
        self.QuestionTussenstapFrame = Frame(self.master)
        self.QuestionTussenstapFrame.grid()
        if self.difficulty != "0":
            Label(self.QuestionTussenstapFrame, text='Tussenstap %s van %s' %(str(self.aantstappendone+1), self.aantstappen), font=("Helvetica", 12)).grid()
            Label(self.QuestionTussenstapFrame, text=self.tussenstap[0][0+int(self.aantstappendone)], font=("Helvetica", 12)).grid()
            self.tussenstapanswer = self.tussenstap[1][0+int(self.aantstappendone)]

    def init_other_frames(self):
        """
        This method initializes the misc. frames, such as the buttons and the Answer entryfield.
        """
        self.QuestionEntryFrame = Frame(self.master)
        self.QuestionEntryFrame.grid() 
        self.textField = Entry(self.QuestionEntryFrame)
        self.textField.grid(row=2, column=2, sticky=N+S+W+E)
        self.textField.bind('<Return>', lambda event: self.validate_answer())
        self.cmdsqrt = Button(self.QuestionEntryFrame, text=SQRT, command=lambda: (self.textField.insert(END, SQRT), self.textField.focus()))
        self.cmdsqrt.grid(row=2, column=0, sticky=N+S+W+E)
        Label(self.QuestionEntryFrame, text="", padx=1).grid(row=2, column=1) #Spacer
       
        self.QuestionValidateFrame = Frame(self.master)
        self.QuestionValidateFrame.grid()
        Label(self.QuestionValidateFrame, text="", pady=1).grid()
        self.cmdValidate = Button(self.QuestionValidateFrame, text="Controleer je antwoord", command=self.validate_answer)
        self.cmdValidate.grid(row=4)
        self.cmdStop = Button(self.QuestionValidateFrame, text="Stop", command=lambda:self.quit())
        self.cmdStop.grid(row=4,column=1)
        self.textField.focus()

    def next_tussenstap(self):
        """
        This method takes care of the pre-initialization of the 'tussenstap' frame.
        It destroys all other frames and then calls init_tussenstap_frame() and init_other_frames().
        """
        self.QuestionTussenstapFrame.destroy()
        self.QuestionEntryFrame.destroy()
        self.QuestionValidateFrame.destroy()
        self.init_tussenstap_frame()
        self.init_other_frames()

    def validate_answer(self):
        """
        This method validates the given answer. First it checks if difficulty is set to 0, which means that there are 'tussenstappen'.
        Then the answer that the user gave is compared with the right answer.
        If they are the same, a messagebox pops up to let the user know he/she gave the correct answer.
        Then the amount of 'tussenstappen' left is checked, if there are no more 'tussenstappen' the window is destroyed.
        If there are more 'tussenstappen', the aantstappendone variable is increased so that init_tussenstap_frame will display the next 'tussenstap'.
        """
        answer = self.textField.get()
        if self.difficulty == "0":
            self.tussenstapanswer = self.tussenstap[1][0+int(self.aantstappen)-1]
            self.aantstappendone = self.aantstappen-1
        if answer == self.tussenstapanswer:
            tkMessageBox.showwarning("Goed zo!", "Je antwoord is correct!")
            if self.aantstappendone == self.aantstappen-1:
                if self.difficulty == "1" and self.aantstappen > 1:
                    tkMessageBox.showwarning("Goed zo!", "Je hebt de oefening succesvol afgerond! Op naar de volgende!")
                self.master.destroy()
            else:
                self.aantstappendone += 1
                self.next_tussenstap()
        else:
            tkMessageBox.showwarning("Jammer","Jammer, het gegeven antwoord is fout. Probeer opnieuw.")

    def quit(self):
        """
        This method is used to properly quit the QuestionWindow. It sets the variable self.nextq to false, so that the question loop
        in main.py is broken and the main menu is shown.
        """
        self.nextq = False
        self.master.destroy()
